//
//  RenderBase3d.m
//  metalloader3D
//
//  Created by lrj on 2023/2/15.
//

#import "RenderBase3d.h"
@import simd;
@import ModelIO;
@import MetalKit;
#import "AAPLMesh.h"
#import "AAPLMathUtilities.h"
#import "ShaderTypes.h"
@implementation RenderBase3d
{
    id <MTLDevice> _device;
    id <MTLCommandQueue> _commandQueue;
    MTLVertexDescriptor *_defaultVertexDescriptor;
    id <MTLRenderPipelineState> _pipelineState;

    id <MTLDepthStencilState> _relaxedDepthState;
    NSArray<AAPLMesh *> *_meshes;

}

- (instancetype)initWithMetalKitView:(MTKView *)view
{
    self = [super init];
    if(self){
        _device = view.device;
        [self loadMetalWithMetalKitView:view];
        [self loadAssets];
    }
    return  self;
}
- (void)loadMetalWithMetalKitView:(nonnull MTKView *)view{
    view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
    view.colorPixelFormat = MTLPixelFormatBGRA8Unorm_sRGB;
    view.sampleCount = 1;
    //sharder language 链接管理器
    id <MTLLibrary> defaultLibrary = [_device newDefaultLibrary];
    _defaultVertexDescriptor = [[MTLVertexDescriptor alloc] init];
    
    // Positions.
    _defaultVertexDescriptor.attributes[0].format = MTLVertexFormatFloat3;
    _defaultVertexDescriptor.attributes[0].offset = 0;
    _defaultVertexDescriptor.attributes[0].bufferIndex = 0;

    // Texture coordinates.
    _defaultVertexDescriptor.attributes[1].format = MTLVertexFormatFloat2;
    _defaultVertexDescriptor.attributes[1].offset = 12;
    _defaultVertexDescriptor.attributes[1].bufferIndex = 0;
    
    
    // ...
    //固定的的步长，虽然目前只有osition和texture 预留位置，材质光照等信息，所以预留44
    _defaultVertexDescriptor.layouts[0].stride = 44;
    _defaultVertexDescriptor.layouts[0].stepRate = 1;
    _defaultVertexDescriptor.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;
    
    id <MTLFunction> vertexStandardMaterial = [defaultLibrary newFunctionWithName:@"vertexShader"];
    id <MTLFunction> fragmentStandardMaterial = [defaultLibrary newFunctionWithName:@"fragmentShader"];
    
    // Create a render pipeline state descriptor.
    MTLRenderPipelineDescriptor * renderPipelineStateDescriptor = [MTLRenderPipelineDescriptor new];
    //标识符号
    renderPipelineStateDescriptor.label = @"Forward Lighting";
    //多重采样
    renderPipelineStateDescriptor.sampleCount = view.sampleCount;
    renderPipelineStateDescriptor.vertexDescriptor = _defaultVertexDescriptor;
    renderPipelineStateDescriptor.vertexFunction = vertexStandardMaterial;
    renderPipelineStateDescriptor.fragmentFunction = fragmentStandardMaterial;
    renderPipelineStateDescriptor.colorAttachments[0].pixelFormat = view.colorPixelFormat;
    renderPipelineStateDescriptor.depthAttachmentPixelFormat = view.depthStencilPixelFormat;
    //模版附件像素格式，比如用喷枪纸板精确绘图，设置一下模版的格式
    renderPipelineStateDescriptor.stencilAttachmentPixelFormat = view.depthStencilPixelFormat;
    
    
    NSError* error = nil;
    _pipelineState = [_device newRenderPipelineStateWithDescriptor:renderPipelineStateDescriptor
                                       error:&error];
    NSAssert(_pipelineState, @"Failed to create pipeline state: %@", error);
    MTLDepthStencilDescriptor *depthStateDesc = [[MTLDepthStencilDescriptor alloc] init];

    {
        depthStateDesc.depthCompareFunction = MTLCompareFunctionLessEqual;
        depthStateDesc.depthWriteEnabled = YES;
        _relaxedDepthState = [_device newDepthStencilStateWithDescriptor:depthStateDesc];
    }
    _commandQueue = [_device newCommandQueue];
    
    
}
// 加载模型
- (void)loadAssets{
    NSError *error = nil;
    MDLVertexDescriptor *modelIOVertexDescriptor =
        MTKModelIOVertexDescriptorFromMetal(_defaultVertexDescriptor);
    //顶点
    modelIOVertexDescriptor.attributes[0].name  = MDLVertexAttributePosition;
    //纹理
    modelIOVertexDescriptor.attributes[1].name  = MDLVertexAttributeTextureCoordinate;
    //各种其他的顶点
    //modelIOVertexDescriptor.attributes[2].name    = MDLVertexAttributeNormal;
    //modelIOVertexDescriptor.attributes[3].name   = MDLVertexAttributeTangent;
    //modelIOVertexDescriptor.attributes[4].name = MDLVertexAttributeBitangent;
    
    NSURL *modelFileURL = [[NSBundle mainBundle] URLForResource:@"Temple.obj" withExtension:nil];
    NSAssert(modelFileURL, @"Could not find model (%@) file in bundle", modelFileURL.absoluteString);
    _meshes = [AAPLMesh newMeshesFromURL:modelFileURL
                 modelIOVertexDescriptor:modelIOVertexDescriptor
                             metalDevice:_device
                                   error:&error];
    
    NSAssert(_meshes, @"Could not find model (%@) file in bundle", error);
}

/// Called whenever the view changes orientation or size.多用于投影视图矩阵的设置
- (void) mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size
{
    
}
- (void)drawInMTKView:(MTKView *)view
{
    id <MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
    commandBuffer.label = @"MyCommand";
    MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
    if(renderPassDescriptor != nil)
    {
        id <MTLRenderCommandEncoder> renderEncoder =
            [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];

        [renderEncoder setCullMode:MTLCullModeBack];

        [renderEncoder pushDebugGroup:@"Render Forward Lighting"];
        [renderEncoder setRenderPipelineState:_pipelineState];
        [renderEncoder setDepthStencilState:_relaxedDepthState];
        [self drawMeshes:renderEncoder];
        [renderEncoder popDebugGroup];

        [renderEncoder endEncoding];
    }
    // Schedule a presentation for the current drawable, after the framebuffer is complete.
    [commandBuffer presentDrawable:view.currentDrawable];

    // Finalize rendering here and send the command buffer to the GPU.
    [commandBuffer commit];
}

/// Draw the mesh objects with the given render command encoder.
- (void)drawMeshes:(id<MTLRenderCommandEncoder>)renderEncoder
{
    //__unsafe_unretained 提高性能，弱引用，当确定对象一定存在的时候
    for (__unsafe_unretained AAPLMesh *mesh in _meshes)
    {
        __unsafe_unretained MTKMesh *metalKitMesh = mesh.metalKitMesh;

        // Set the mesh's vertex buffers.
        for (NSUInteger bufferIndex = 0; bufferIndex < metalKitMesh.vertexBuffers.count; bufferIndex++)
        {
            __unsafe_unretained MTKMeshBuffer *vertexBuffer = metalKitMesh.vertexBuffers[bufferIndex];
            if((NSNull*)vertexBuffer != [NSNull null])
            {
                [renderEncoder setVertexBuffer:vertexBuffer.buffer
                                        offset:vertexBuffer.offset
                                       atIndex:bufferIndex];
            }
        }

        // Draw each submesh of the mesh.
        for(AAPLSubmesh *submesh in mesh.submeshes)
        {
            // Set any textures that you read or sample in the render pipeline.
            [renderEncoder setFragmentTexture:submesh.textures[0]
                                      atIndex:0];

            [renderEncoder setFragmentTexture:submesh.textures[1]
                                      atIndex:1];

            [renderEncoder setFragmentTexture:submesh.textures[2]
                                      atIndex:2];

            MTKSubmesh *metalKitSubmesh = submesh.metalKitSubmmesh;

            //索引绘制
            [renderEncoder drawIndexedPrimitives:metalKitSubmesh.primitiveType
                                      indexCount:metalKitSubmesh.indexCount
                                       indexType:metalKitSubmesh.indexType
                                     indexBuffer:metalKitSubmesh.indexBuffer.buffer
                               indexBufferOffset:metalKitSubmesh.indexBuffer.offset];
        }
    }
}


@end
